package org.jetlinks.pro.assets.impl;

import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.web.authorization.Authentication;
import org.hswebframework.web.authorization.DefaultDimensionType;
import org.hswebframework.web.authorization.simple.SimpleDimension;
import org.jetlinks.pro.assets.AssetBindManager;
import org.jetlinks.pro.assets.AssetManager;
import org.jetlinks.pro.assets.AssetsHolder;
import org.jetlinks.pro.assets.AssetsHolderProvider;
import org.reactivestreams.Publisher;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collections;

/**
 * 实现{@link AssetsHolderProvider},
 * 基于维度的资产权限持有者,通过获取权限信息{@link org.hswebframework.web.authorization.Authentication}
 * 中的维度信息{@link Authentication#getDimensions()}来进行权限控制.
 *
 * @author zhouhao
 * @see DimensionAssetsHolder
 * @since 1.11
 */
@AllArgsConstructor
@Component
public class DimensionAssetsHolderProvider implements AssetsHolderProvider {

    //资产管理器
    private final AssetManager assetManager;

    //资产绑定管理器
    private final AssetBindManager bindManager;

    //维度资产控制配置
    private final DimensionAssetsProperties properties;

    @Override
    public Mono<DimensionAssetsHolder> createHolder(Authentication authentication) {
        //需要在配置文件中指定维度
        if (CollectionUtils.isEmpty(properties.getEnabledDimensions())) {
            return Mono.empty();
        }
        return Flux
            .fromIterable(properties.getEnabledDimensions())
            .flatMapIterable(authentication::getDimensions)
            .as(flux -> {
                //补丁: 指定了user但是维度里没有user,通常出现在自定义权限策略时.
                if (properties.getEnabledDimensions().contains("user")
                    && !authentication.hasDimension("user")) {
                    return Flux
                        .concat(
                            flux,
                            //手动补偿一个用户维度
                            Mono.just(SimpleDimension.of(authentication.getUser().getId(),
                                                         authentication.getUser().getName(),
                                                         DefaultDimensionType.user,
                                                         Collections.emptyMap()))
                        );
                }
                return flux;
            })
            .collectList()
            .filter(CollectionUtils::isNotEmpty)
            //返回DimensionAssetsHolder
            .map(dimensions -> new DimensionAssetsHolder(dimensions,
                                                         assetManager,
                                                         bindManager,
                                                         properties.isAutoBinding(),
                                                         authentication.getUser().getId(),
                                                         properties.getDimensionExcludeAssetTypes()));
    }
}
